//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.ObjectModel;
using Microsoft.Research.ScientificWorkflow.TridentModel;
using Microsoft.Research.ScientificWorkflow.TridentUtilities;
using SR = Microsoft.Research.DataLayer;

namespace Microsoft.Research.ScientificWorkflow.TridentComposer
{
    /// <summary>
    /// A proxy class which saves the workflow opened from filesystem to registry in another appdomain.
    /// </summary>
    public class SaveWorkflowFromFileSystemProxy : MarshalByRefObject
    {
        private SR.Connection registryConnection;

        /// <summary>
        /// Dummy constructor. Needed to create this object by CreateInstanceAndUnWrap method.
        /// </summary>
        public SaveWorkflowFromFileSystemProxy()
        {
        }

        /// <summary>
        /// Initializes a new instance of the proxy
        /// </summary>
        /// <param name="registryConnection">The registry connection.</param>
        public SaveWorkflowFromFileSystemProxy(SR.Connection registryConnection)
        {
            this.registryConnection = registryConnection;
        }

        /// <summary>
        /// Save the workflow to registry.
        /// </summary>
        /// <param name="fileReferences">The file references.</param>
        /// <param name="workflowModel">The workflow model.</param>
        /// <param name="workflowName">The workflow name.</param>
        /// <param name="userName">The user name.</param>
        /// <returns>The workflow model.</returns>
        public TridentWorkflowModel Execute(Collection<FileReference> fileReferences, TridentWorkflowModel workflowModel, string workflowName, string userName)
        {
            AppDomain childAppdomain = null;
            try
            {
                childAppdomain = this.CreateAppDomain();
                
                SaveWorkflowFromFileSystemProxy proxyObject = childAppdomain.CreateInstanceAndUnwrap
                    ("Microsoft.Research.ScientificWorkflow.TridentComposer",
                    "Microsoft.Research.ScientificWorkflow.TridentComposer.SaveWorkflowFromFileSystemProxy") as SaveWorkflowFromFileSystemProxy;
                proxyObject.Init(this.registryConnection);
                return proxyObject.SaveWorkflow(fileReferences, workflowModel, workflowName, userName);
            }
            finally
            {
                UnloadAppDomain(childAppdomain);
            }
        }

        /// <summary>
        /// Initialize the new object created in another appdomain.
        /// </summary>
        /// <param name="connection">The registry connection.</param>
        private void Init(SR.Connection connection)
        {
            this.registryConnection = connection;
            SR.SR_Connection.Init();
            this.registryConnection.Open();
            this.registryConnection.AutoSave = false;

            // Authenticate the user.
            try
            {
                TridentAuthentication authenticate = new TridentAuthentication(this.registryConnection);
                authenticate.AuthenticateUser();
            }
            catch (TridentCustomException)
            {
                throw;
            }
        }

        /// <summary>
        /// Save workflow.
        /// </summary>
        /// <param name="fileReferences">The file reference.</param>
        /// <param name="workflowModel">The workflow model to save.</param>
        /// <param name="workflowName">Workflow name.</param>
        /// <param name="userName">The user name.</param>
        /// <returns>The trident workflow model.</returns>
        private TridentWorkflowModel SaveWorkflow(Collection<FileReference> fileReferences, TridentWorkflowModel workflowModel, string workflowName, string userName)
        {
            ActivityComposer activityComposer = new ActivityComposer(this.registryConnection, false);
            WorkflowComposer composer = new WorkflowComposer(this.registryConnection, activityComposer);
            return composer.SaveWorkflowFromFileSystemInRegistry(workflowName, workflowModel, fileReferences, userName);
        }

        /// <summary>
        /// Creates a new app domain.
        /// </summary>
        /// <returns>New instance of the app domain.</returns>
        private AppDomain CreateAppDomain()
        {
            AppDomain newDomain = AppDomain.CreateDomain("SaveWorkflowFromFileSystemAppdomain");
            newDomain.UnhandledException += new UnhandledExceptionEventHandler(AppDomainUnhandledException);
            return newDomain;
        }

        /// <summary>
        /// Unloads the app domain.
        /// </summary>
        /// <param name="appDomain">Instance of the AppDomain.</param>
        private static void UnloadAppDomain(AppDomain appDomain)
        {
            if (appDomain != null)
            {
                AppDomain.Unload(appDomain);
            }
        }

        /// <summary>
        /// Method logs the unhandled exception in the app domain.
        /// </summary>
        /// <param name="sender">Object who raises the unhandled exception.</param>
        /// <param name="e">Event argument containing exception details.</param>
        private void AppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            // No-op.
        }
    }
}
